import 'package:timezone/timezone.dart';

class Moment {
  late DateTime dateTime;

  Moment([Object? dateTime]) {
    this.dateTime = _parse(dateTime);
  }

  Moment.utc([Object? dateTime]) {
    this.dateTime = _parseUtc(dateTime);
  }

  // 格式化时长
  static String duration(Duration duration, String format) {
    bool root = true;
    // 日->DD/D、时-> HH/H/hh/h、分 -> mm/m、秒 -> ss/s
    if (format.contains('DD')) {
      int days = duration.inDays.abs();
      format = format.replaceAll('DD', days < 10 ? '0$days' : '$days');
      root = false;
    } else if (format.contains('D')) {
      format = format.replaceAll('D', '${duration.inDays.abs()}');
      root = false;
    }
    if (format.contains('HH') || format.contains('hh')) {
      int hours = root ? duration.inHours.abs() : duration.inHours.remainder(24).abs();
      format = format.replaceAll(RegExp(r'HH|hh'), hours < 10 ? '0$hours' : '$hours');
      root = false;
    } else if (format.contains('H') | format.contains('h')) {
      int hours = root ? duration.inHours.abs() : duration.inHours.remainder(24).abs();
      format = format.replaceAll(RegExp(r'H|h'), '$hours');
      root = false;
    }
    if (format.contains('mm')) {
      int minutes = root ? duration.inMinutes.abs() : duration.inMinutes.remainder(60).abs();
      format = format.replaceAll('mm', minutes < 10 ? '0$minutes' : '$minutes');
      root = false;
    } else if (format.contains('m')) {
      int minutes = root ? duration.inMinutes.abs() : duration.inMinutes.remainder(60).abs();
      format = format.replaceAll('m', '$minutes');
      root = false;
    }
    if (format.contains('ss')) {
      int seconds = root ? duration.inSeconds.abs() : duration.inSeconds.remainder(60).abs();
      format = format.replaceAll('ss', seconds < 10 ? '0$seconds' : '$seconds');
    } else if (format.contains('s')) {
      int seconds = root ? duration.inSeconds.abs() : duration.inSeconds.remainder(60).abs();
      format = format.replaceAll('s', '$seconds');
    }
    return format;
  }

  // 解析时间
  DateTime _parse(Object? dateTime) {
    try {
      if (dateTime is String) {
        return DateTime.parse(dateTime);
      }
      if (dateTime is int) {
        return DateTime.fromMillisecondsSinceEpoch(dateTime);
      }
      if (dateTime is DateTime) {
        return dateTime;
      }
      if (dateTime is Moment) {
        return dateTime.toDate();
      }
      return DateTime.now();
    } catch (e) {
      print('Moment日期时间解析失败: $e');
      return DateTime.now();
    }
  }

  // 解析UTC时间
  DateTime _parseUtc(Object? dateTime) {
    try {
      if (dateTime is String) {
        Duration offset = DateTime.parse(dateTime).timeZoneOffset;
        if (offset == Duration.zero) {
          return DateTime.parse(dateTime);
        }
        return DateTime.parse(dateTime).add(offset).toUtc();
      }
      if (dateTime is int) {
        return DateTime.fromMillisecondsSinceEpoch(dateTime).toUtc();
      }
      if (dateTime is DateTime) {
        return dateTime.toUtc();
      }
      if (dateTime is Moment) {
        return dateTime.toDate().toUtc();
      }
      if (dateTime is Duration) {
        DateTime epoch = DateTime.fromMillisecondsSinceEpoch(0);
        return epoch.add(dateTime);
      }
      return DateTime.now().toUtc();
    } catch (e) {
      print('Moment日期时间解析失败: $e');
      return DateTime.now().toUtc();
    }
  }

  // 日期格式化：年->YYYY/YY、月->MM/M、日->DD/D、时->HH/H/hh/h、分->mm/m、秒->ss/s、上午下午->A/a
  String format([String? format]) {
    if (format == null) {
      return dateTime.toString();
    }
    format = _yearFormat(dateTime.year, format);
    format = _comFormat(dateTime.month, format, 'MM', 'M');
    format = _comFormat(dateTime.day, format, 'DD', 'D');
    format = _comFormat(dateTime.hour, format, 'HH', 'H');
    format = _ampmFormat(dateTime.hour, format);
    int hour = dateTime.hour < 12 ? dateTime.hour : dateTime.hour - 12;
    format = _comFormat(hour, format, 'hh', 'h');
    format = _comFormat(dateTime.minute, format, 'mm', 'm');
    format = _comFormat(dateTime.second, format, 'ss', 's');
    return format;
  }

  String _yearFormat(int value, String format) {
    if (format.contains('YYYY')) {
      format = format.replaceAll('YYYY', '$value');
    } else if (format.contains('YY')) {
      String val = '$value'.substring('$value'.length - 2, '$value'.length);
      format = format.replaceAll('YY', val);
    }
    return format;
  }

  String _comFormat(int value, String format, String full, String single) {
    if (format.contains(full)) {
      format = format.replaceAll(full, value < 10 ? '0$value' : '$value');
    } else if (format.contains(single)) {
      format = format.replaceAll(single, '$value');
    }
    return format;
  }

  String _ampmFormat(int value, String format) {
    if (format.contains('A')) {
      format = format.replaceAll('A', value < 12 ? 'AM' : 'PM');
    } else if (format.contains('a')) {
      format = format.replaceAll('a', value < 12 ? 'am' : 'pm');
    }
    return format;
  }

  // 获取时间戳
  int unix() {
    return dateTime.millisecondsSinceEpoch;
  }

  // 转换为UT时间
  Moment toUtc() {
    dateTime = dateTime.toUtc();
    return this;
  }

  // 转换为本地时间
  Moment toLocal() {
    dateTime = dateTime.toLocal();
    return this;
  }

  // 获取DateTime
  DateTime toDate() {
    return dateTime;
  }

  // 更改时区
  Moment tz(String locationName, [force = false]) {
    final location = getLocation(locationName);
    if (force) {
      Duration offset = dateTime.timeZoneOffset;
      if (offset != Duration.zero) {
        dateTime = dateTime.add(offset).toUtc();
      }
      TZDateTime tzDateTime = TZDateTime.from(dateTime, location);
      Duration timeZoneOffset = tzDateTime.timeZoneOffset;
      dateTime = tzDateTime.subtract(timeZoneOffset);
    } else {
      TZDateTime tzDateTime = TZDateTime.from(dateTime, location);
      dateTime = tzDateTime;
    }
    return this;
  }

  // 加法
  Moment add(Object? value, [String? unit]) {
    if (value is Duration) {
      dateTime = dateTime.add(value);
    } else if (value is int && unit != null) {
      Duration? duration = _parseDuration(value, unit);
      if (duration != null) {
        dateTime = dateTime.add(duration);
      }
    }
    return this;
  }

  // 减法
  Moment subtract(Object? value, [String? unit]) {
    if (value is Duration) {
      dateTime = dateTime.subtract(value);
    } else if (value is int && unit != null) {
      Duration? duration = _parseDuration(value, unit);
      if (duration != null) {
        dateTime = dateTime.subtract(duration);
      }
    }
    return this;
  }

  Duration? _parseDuration(int value, String unit) {
    switch (unit) {
      case 'days':
      case 'd':
        return Duration(days: value);
      case 'hours':
      case 'h':
        return Duration(hours: value);
      case 'minutes':
      case 'm':
        return Duration(minutes: value);
      case 'seconds':
      case 's':
        return Duration(seconds: value);
      case 'milliseconds':
      case 'ms':
        return Duration(milliseconds: value);
      default:
        return null;
    }
  }

  // 获取时差
  Duration difference(Object? dateTime) {
    return this.dateTime.difference(_parse(dateTime));
  }

  // 是否之前
  bool isBefore(Object? dateTime) {
    return this.dateTime.isBefore(_parse(dateTime));
  }

  // 是否之后
  bool isAfter(Object? dateTime) {
    return this.dateTime.isAfter(_parse(dateTime));
  }

  // 是否之间
  bool isBetween(Object? startTime, Object? endTime) {
    return dateTime.isAfter(_parse(startTime)) && dateTime.isBefore(_parse(endTime));
  }

  // 是否相同
  bool isSame(Object? dateTime, String unit) {
    DateTime compare = _parse(dateTime);
    switch (unit) {
      case 'year':
      case 'y':
        return this.dateTime.year == compare.year;
      case 'month':
      case 'M':
        return this.dateTime.year == compare.year && this.dateTime.month == compare.month;
      case 'day':
      case 'd':
        return this.dateTime.year == compare.year &&
            this.dateTime.month == compare.month &&
            this.dateTime.day == compare.day;
      case 'hour':
      case 'h':
        return this.dateTime.year == compare.year &&
            this.dateTime.month == compare.month &&
            this.dateTime.day == compare.day &&
            this.dateTime.hour == compare.hour;
      case 'minute':
      case 'm':
        return this.dateTime.year == compare.year &&
            this.dateTime.month == compare.month &&
            this.dateTime.day == compare.day &&
            this.dateTime.hour == compare.hour &&
            this.dateTime.minute == compare.minute;
      case 'second':
      case 's':
        return this.dateTime.year == compare.year &&
            this.dateTime.month == compare.month &&
            this.dateTime.day == compare.day &&
            this.dateTime.hour == compare.hour &&
            this.dateTime.minute == compare.minute &&
            this.dateTime.second == compare.second;
      case 'millisecond':
      case 'ms':
        return this.dateTime.millisecondsSinceEpoch == compare.millisecondsSinceEpoch;
      default:
        return this.dateTime.isAtSameMomentAs(compare);
    }
  }
}
